with Logging;
with Element; use Element;
package body PES is

   function Create(Block : in Block_Type; Time_Code : in Unsigned_32) return PES_Type is
      Result : PES_Type;
   begin
      if Block.Size > Element_Size_Type( PES_SIZE - (PES_HEADER_SIZE + PES_HEADER_EXT + PES_HEADER_PTS )) then
         Result.Size := PES_SIZE - (PES_HEADER_SIZE + PES_HEADER_EXT + PES_HEADER_PTS);
         Logging.Error("Block too big for PES. Size was: " & Element_Size_Type'Image(Block.Size));
      else
         Result.Size := Natural(Block.Size);
         Logging.Info("Creating short PES size: " & Natural'Image(Result.Size) & " + header" );
      end if;

      Result.Start_Pos := Block.File_Index;
      Result.Header.Stream_Id := Stream_ID;

      -- do we have an access unit
      if Block.Header.Key_Frame then
         Result.Extension.Data_Alignment := 1;
         Result.Extension.PTS_DTS := (1,0);
         Result.Extension.PTS_Field := Time_Code;
         Result.Extension.PES_Header_Data_Length := Byte_Type(PES_HEADER_SIZE + PES_HEADER_EXT + PES_HEADER_PTS);
         Result.Header.Length := Word_16(Result.Size + PES_HEADER_EXT + PES_HEADER_PTS);
      else
         Result.Extension.PES_Header_Data_Length := Byte_Type(PES_HEADER_SIZE + PES_HEADER_EXT);
         Result.Header.Length := Word_16(Result.Size + PES_HEADER_EXT);
      end if;
      return Result;
   end Create;

--     procedure PES_Generator(Buffer : in out PES_Buffer_Type; PTS_Counter : in Unsigned_32) is
--        PES_SIZE : constant Positive := (2**16) - 1; -- max bytes
--        PES_HEADER_SIZE : constant Positive := 6; -- header bytes
--        PES_HEADER_EXT : constant Positive := 3; -- extension bytes
--        PES_HEADER_PTS : constant Positive := 5; -- PTS bytes
--        PES_HEADER_DTS : constant Positive := 5; -- DTS bytes
--        Stream_ID : constant Byte_Type := 16#06#;   -- private stream 16#bd# (1011 1101)
--      -- ATSC descriptor tag stream_type - PMT types 1000 0001 (0x81) DTS-0x85
--      -- DVB-T 0x06 + descriptor
--        Remaining_Bytes : Natural := Buffer.Access_Unit.Size;
--     begin
--        -- reset PES buffer (we know there is at least one)
--        Buffer.Size := 1;
--
--        -- create first PES
--        Buffer.PES_Units(Buffer.Size).Start_Pos := Buffer.Access_Unit.Start_Pos;
--        Buffer.PES_Units(Buffer.Size).Header.Stream_Id := Stream_ID;
--
--        if Buffer.Access_Unit.Is_Access_Unit then
--           Buffer.PES_Units(Buffer.Size).Extension.Data_Alignment := 1;
--           Buffer.PES_Units(Buffer.Size).Extension.PTS_DTS := (1,0);
--           Buffer.PES_Units(Buffer.Size).Extension.PTS_Field := PTS_Counter;
--           Buffer.PES_Units(Buffer.Size).Extension.PES_Header_Data_Length := Byte_Type(PES_HEADER_SIZE + PES_HEADER_EXT + PES_HEADER_PTS);
--           if Buffer.Access_Unit.Size <= PES_SIZE - (PES_HEADER_SIZE + PES_HEADER_EXT + PES_HEADER_PTS) then
--              -- only one PES
--              Buffer.PES_Units(Buffer.Size).Size := Buffer.Access_Unit.Size;
--              Buffer.PES_Units(Buffer.Size).Header.Length := Word_16(Buffer.Access_Unit.Size + PES_HEADER_EXT + PES_HEADER_PTS);
--              Logging.Info("Creating short PES size: " & Natural'Image(Buffer.PES_Units(Buffer.Size).Size) & " + header" ));
--              return;
--           else
--              -- full pes (more PESs)
--              Buffer.PES_Units(Buffer.Size).Size := PES_SIZE - (PES_HEADER_SIZE + PES_HEADER_EXT + PES_HEADER_PTS);
--              Buffer.PES_Units(Buffer.Size).Header.Length := Word_16(PES_SIZE - PES_HEADER_SIZE);
--              Remaining_Bytes := Remaining_Bytes - Buffer.PES_Units(1).Size;
--              Logging.Info("Creating full PES size: " & Natural'Image(Buffer.PES_Units(Buffer.Size).Size) & " + header"));
--           end if;
--        else
--           Buffer.Size := 0; -- rewind size (first PES was not an access unit)
--        end if;
--
--        loop
--           Buffer.Size := Buffer.Size + 1;
--           -- create the remaining PESs
--
--           Buffer.PES_Units(Buffer.Size).Header.Stream_Id := Stream_ID;
--           Buffer.PES_Units(Buffer.Size).Start_Pos := Buffer.Access_Unit.Start_Pos + ((Buffer.Size - 1) * PES_SIZE);
--           Buffer.PES_Units(Buffer.Size).Extension.PES_Header_Data_Length := Byte_Type(PES_HEADER_SIZE + PES_HEADER_EXT);
--           if Remaining_Bytes <= PES_SIZE - (PES_HEADER_SIZE + PES_HEADER_EXT) then
--              -- last PES
--              Buffer.PES_Units(Buffer.Size).Size := Remaining_Bytes;
--              Buffer.PES_Units(Buffer.Size).Header.Length := Word_16(Remaining_Bytes + PES_HEADER_EXT);
--              Logging.Info("Creating short PES size: " & Natural'Image(Buffer.PES_Units(Buffer.Size).Size) & " + header"));
--              return;
--           else
--              -- full pes (more PESs)
--              Buffer.PES_Units(Buffer.Size).Size := PES_SIZE - (PES_HEADER_SIZE + PES_HEADER_EXT);
--              Buffer.PES_Units(Buffer.Size).Header.Length := Word_16(PES_SIZE - PES_HEADER_SIZE);
--              Remaining_Bytes := Remaining_Bytes - Buffer.PES_Units(Buffer.Size).Size;
--              Logging.Info("Creating full PES size: " & Natural'Image(Buffer.PES_Units(Buffer.Size).Size) & " + header"));
--           end if;
--        end loop;
--
--
--     end PES_Generator;

   procedure Write_PES_Header_Extension_Type(Stream : not null access Ada.Streams.Root_Stream_Type'Class; Item : in PES_Header_Extension_Type) is
      Result : Byte_Type := 0;
   begin

      Result := Result or Byte_Type(Item.Start_Code(1)) * 2**7;
      Result := Result or Byte_Type(Item.Start_Code(2)) * 2**6;
      Result := Result or Byte_Type(Item.Scramblin_Contr(1)) * 2**5;
      Result := Result or Byte_Type(Item.Scramblin_Contr(2)) * 2**4;
      Result := Result or Byte_Type(Item.Priority) * 2**3;
      Result := Result or Byte_Type(Item.Data_Alignment) * 2**2;
      Result := Result or Byte_Type(Item.Copyright) * 2**1;
      Result := Result or Byte_Type(Item.Orig_Copy) * 2**0;
      Byte_Type'Write(Stream, Result);

      Result := 0;
      Result := Result or Byte_Type(Item.PTS_DTS(1)) * 2**7;
      Result := Result or Byte_Type(Item.PTS_DTS(2)) * 2**6;
      Result := Result or Byte_Type(Item.ESCR) * 2**5;
      Result := Result or Byte_Type(Item.ES_Rate) * 2**4;
      Result := Result or Byte_Type(Item.DSM_Mode) * 2**3;
      Result := Result or Byte_Type(Item.Copy_Info) * 2**2;
      Result := Result or Byte_Type(Item.PES_CRC) * 2**1;
      Result := Result or Byte_Type(Item.PES_Extension) * 2**0;
      Byte_Type'Write(Stream, Result);

      Byte_Type'Write(Stream, Item.PES_Header_Data_Length);

      Write_PES_Header_PTS_DTS_Type(Stream, Item);

   end Write_PES_Header_Extension_Type;

   procedure Write_PES_Header_PTS_DTS_Type(Stream : not null access Ada.Streams.Root_Stream_Type'Class; Item : in PES_Header_Extension_Type) is
      procedure Write(Header : in Byte_Type; Value : in Unsigned_32) is
         Mask : Unsigned_32 := 0;
      begin
         Byte_Type'Write(Stream, Header or Byte_Type(Shift_Right(Value, 29)));

         Mask := 2#00000000000000000000000011111111#;
         Byte_Type'Write(Stream, Byte_Type(Mask and Shift_Right(Value, 22)));


         Mask := 2#00000000000000000000000011111110#;
         Byte_Type'Write(Stream, 2#00000001# or Byte_Type(Mask and Shift_Right(Value, 14)));

         Mask := 2#00000000000000000000000011111111#;
         Byte_Type'Write(Stream, Byte_Type(Mask and Shift_Right(Value, 7)));

         Mask := 2#00000000000000000000000011111110#;
         Byte_Type'Write(Stream, 2#00000001# or Byte_Type(Mask and Shift_Left(Value, 1)));

      end Write;
      pragma inline(Write);
   begin
      -- set the initial mask
      if Item.PTS_DTS(1) = 1 and Item.PTS_DTS(2) = 1 then
         Write(2#00110001#, Item.PTS_Field);
         Write(2#00010001#, Item.DTS_Field);
      elsif Item.PTS_DTS(1) = 1 and Item.PTS_DTS(2) = 0 then
         Write(2#00100001#, Item.PTS_Field);
      end if;

   end Write_PES_Header_PTS_DTS_Type;
   pragma inline(Write_PES_Header_PTS_DTS_Type);

end PES;